/*
 * @Author: Zyunchao 18651805393@163.com
 * @Date: 2022-03-12 17:11:44
 * @LastEditors: Zyunchao 18651805393@163.com
 * @LastEditTime: 2023-06-16 09:56:27
 * @FilePath: /micro-temp-monorepo/_core/router/backEnd/micro-app.ts
 * @Description: 微应用后台路由注册核心
 */
import store from '@jsjn/micro-core-store'
import packInfo from '@ROOT/package.json'
import { Router } from 'vue-router'
import { ElMessage } from 'element-plus'
import _ from 'lodash'
// import currentMicroAppId, {
//     CURRENT_MICRO_APP_ID_CACHE
// } from '@jsjn/micro-core-micro-main/micro-app/currentMicroTagName'
// import { Session } from '@jsjn/utils'

/**
 * 后端路由创建
 * 在进入微应用的路由之前，基座应用的路由一定是创建好的
 * 所以微应用在初始化时，就可以拿到基座应用中的所有已注册路由了
 * 因此，在微应用中，后台路由的处理是同步的
 */
export default (router: Router) => {
    /**
     * remoteRegisteredRoutes 中包含了所有应用的路由
     * - 基座应用
     * - 当前微应用
     * - 其他兄弟微应用
     */
    const remoteRegisteredRoutes = _.cloneDeep(store.state.appFuncTree.remoteRegisteredRoutes)

    /**
     * 筛选出当前应用的路由
     * - 以系统 name 开头
     * - 排除基座应用注册的保险路由（xxx/:page*）
     */
    const currentSysRoutes = remoteRegisteredRoutes.filter(
        (route) => route.path.startsWith(`/${packInfo.name}`) && !route.path.endsWith(':page*')
    )

    /**
     * 转换
     * - path 需要将死的系统名称，替换成微应用的 __MICRO_APP_BASE_ROUTE__
     * - 由基座应用传递过来的路由，组件是微应用的根组件，需要替换成当前应用的组件
     * - 如果是嵌套应用，则需要替换成当前系统的 microAppRoot
     */
    const registeredRoutes = currentSysRoutes.map((route) => {
        const componentPath = route.meta.componentPath
        const pathSplit = componentPath.split('/views/')
        const pageFile = pathSplit[1]

        /**
         * 在注册路由时，只会关注微应用的名称部分，但是微应用会运行在不用的环境中，并且带有不同的前缀
         *  - 流程工厂：`${prefix}/${app.name}/xxxx`
         *  - 普通容器：`${app.name}/xxx`
         *
         * 所以，这里要将基座应用传递带有项目前缀的路径替换成 __MICRO_APP_BASE_ROUTE__
         */
        route.path = route.path.replace(`/${packInfo.name}`, window['__MICRO_APP_BASE_ROUTE__'])

        // 如果页面标识模糊匹配，需要添加 /:page* 的 params
        if (route.meta.isBlurry) {
            route.path = `${route.path}/:page*`
        }

        /**
         * 替换当前系统的 filePath
         * 具体的微应用在引用当前模块时，会解析 @ 符号
         * 每个微应用的 vue.config.js 里都将 @ 别名配置为本地文件夹
         * 所以，这里直接使用 @/ 可以定位到自己的文件夹下
         * 相当于自适应匹配
         */
        route['component'] = () =>
            // 1. 首先加载项目本地的 views 目录下的文件
            import(`@/views/${pageFile}`)
                // 2. 如果未加载到，就去公用页面加载
                .catch(() => import(`@jsjn/common-module/views/${pageFile}`))
                // 3. 公用模块也没有，则抛出错误页面
                .catch((err) => {
                    /**
                     * 错误流转现象记录：
                     *
                     * 在同一个微应用的页面之间切换时，如果基座把微应用容器销毁（如：route-view 的 key 促使路由容器重绘，就会导致微应用容器重绘）
                     * 微应用容器被销毁，微应用就会执行卸载
                     * 在如上情况下，页面切换首先会触发微应用的路由变化，路由变化意味着加载组件
                     * 但是在微应用路由切换的间隙，当前微应用已被卸载，路由会进入当前 catch 状态，本质上是无意义的
                     * 进入 catch 弹出了错误框，由于这次（上一次）的微应用已被卸载，错误页面也加载不到
                     * 就产生了弹出错误框，但是页面正常加载的现象
                     *
                     * 这里将每次的微应用 id 缓存起来，每次运行时，如果与缓存中的微应用 id 不一致，意味着已被销毁，则不予执行后续操作
                     */
                    // const preMicroAppId = Session.get(CURRENT_MICRO_APP_ID_CACHE)
                    // if (preMicroAppId !== currentMicroAppId) return

                    ElMessage.error({
                        message: `${componentPath} 加载失败，请核对路径是否正确`,
                        type: 'error',
                        duration: 0,
                        showClose: true
                    })
                    return import('@jsjn/micro-core-views/error/pageLoadErr.vue')
                })

        // 更新重新注册
        router.hasRoute(route.name) && router.removeRoute(route.name)
        router.addRoute(route as any)
        return route
    })
    store.commit('appFuncTree/setRegisteredRoutes', registeredRoutes)
}
